﻿/**
 * Orderjs - Order js in order.
 * http://soofound.com
 * 
 * Copyright 2013, prcjack
 * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
 *
 * Orderjs is a file(js, css) and module loader. 
 * It loads modules asynchronously, but executes them in sequence ordered by user.
 */

(function(){
var orderjsRoot,
	glob = this, //global object(window in the browser, or exports on the server).
	originalOrderjs = glob.orderjs,
	emptyFn = new Function,
	rRelPath = /\.\.\//,
	rCurrentPath = /\.\//g,
	rTrim = /(^\s+)|\s+$/g,
	docHead = document.getElementsByTagName('HEAD')[0],
	location = document.location;

/**
 * Global name of framework. Shortcuts to #orderjs.moduleMgr.require.
 */
function orderjs(names){
	return moduleMgr.require.apply(moduleMgr, arguments);
}
// helper methods
function ext(dest, source) {
	for (var i in source) dest[i] = source[i];
	return dest;
}

function isArray(obj){
	return !!(obj && obj.constructor === Array);
}

function isFunction(obj){            
	return typeof obj == "function";
}

function isObject(obj){            
	return (obj && typeof obj == "object");
}

function isString(obj){            
	return (typeof obj === "string" || (obj && obj.constructor === String));
}

function isNonNullStr(obj){
	return isString(obj) && (String(obj) !== "");
}

function trim(text){
	return text == null ? "" : text.replace(rTrim , "");
}

function each(obj, iterator, scope) {
	if(!isObject(obj)) return;
	for (var i = 0, len = obj.length; i < len; i++) {
		if(iterator.call(scope, obj[i], i, obj) === false) return;
	}
}

function map(obj, iterator, scope) {
	var results = [];
	
	each(obj, function(item, index){
		results.push(iterator.call(scope, item, index, obj));
	});
	
	return results;
}

function filter(obj, iterator, scope) {
	var results = [];
	
	each(obj, function(item, index){
		if(iterator.call(scope, item, index, obj)) results.push(item);
	});
	
	return results;
}

function bind(f, scope){
	return function(){
		return f.apply(scope, arguments);
	};
}

/**
 * Trim dotSegment("./") of path: "a/././b.html" -> "a/b.html"
 */
function trimDotSegment(path){
	//path.split(rRelPath) is not work under ie6.
	return map(path.split("../"), function(seg){
		return seg.replace(rCurrentPath, "");
	}).join("../");
}

function absolutizePath(path, root){
	path = trimDotSegment(path);
	
	if(path.indexOf("/") === 0){//e.g. : "/webApp/login.do"
		return location.protocol + "//" + location.host + path;
	}
	if(path.indexOf(":") > -1){//e.g. : "http:192.168.2.1/"
		return path; 
	}
	
	//if orderjsRoot is undefined yet, use location.href instead.
	var list = (root || orderjsRoot || location.href).split("/");
	list.pop();
	while (rRelPath.test(path)){
		list.pop();
		path = path.replace(rRelPath, "");
	}
	return list.join("/") + "/" + path;
}

var scripts = document.getElementsByTagName("script"),
	orderjsNode = document.getElementById("orderjsNode") || scripts[scripts.length - 1],
	orderjsSrc = absolutizePath(orderjsNode.getAttribute("src")),
	dataMain = orderjsNode.getAttribute("data-main");

orderjsRoot = orderjsSrc.substr(0, (orderjsSrc.lastIndexOf("/") + 1));
if(!docHead) docHead = orderjsNode.parentNode;

var	isSafari = !!(navigator.userAgent.toLowerCase().indexOf('safari') > -1),
	isOpera = !!window.opera,
	observeReadyState = (document.attachEvent &&
		!(document.attachEvent.toString &&
		document.attachEvent.toString().indexOf('[native code') < 0) && !isOpera),
	linkObservable = !isSafari;

var fileLoader = orderjs.fileLoader = {
	_startedUris : {},
	
	_cacheBuster : new Date().getTime(),
	
	load : function(uri, isCss, onLoad){
		if(!isString(uri)) return null;
		if(this._startedUris[uri]) return ;
		this._startedUris[uri] = true;
		var loadEle;
		
		if(orderjs.config("disableCache")){
			uri += ((uri.indexOf("?") > -1) ? "" : "?") + "_random=" + this._cacheBuster;
		}
		loadEle = isCss ? this.createLink(uri) : this.createScript(uri);
		
		this.observeLoad(loadEle, isCss, onLoad);
		docHead.appendChild(loadEle);
		
		return loadEle;
	},
	
	createScript : function(uri){
		var ele = document.createElement("script");
		
		ele.type = "text/javascript";
		ele.defer = ele.async = true;
		ele.src = uri;
		
		return ele;
	},
	
	createLink : function(uri){


		var ele = document.createElement("link");
		
		ele.type = "text/css"; 
		ele.rel = "stylesheet";
		ele.defer = ele.async = true;
		ele.href = uri;
		
		return ele;
	},
	
	observeLoad : function(ele, isCss, onLoad){
		var me = this,
			loadHandler = (function(){
				var args = arguments;
				return function(){ return me.loadHandler.apply(me, args);};
			})(ele, isCss, onLoad);
		
		if (observeReadyState) {
			ele.attachEvent('onreadystatechange', loadHandler);
		} else {
			if(isCss && linkObservable){
				this.pollCss(ele, loadHandler);
			}
			else{
				ele.addEventListener('load', loadHandler, false);
				ele.addEventListener('error', loadHandler, false);
			}
		}
	},
	
	loadHandler : function(ele, isCss, onLoad){
		if (!ele.readyState || ele.readyState === 'loaded' || ele.readyState === 'complete') {
			if(observeReadyState){
				ele.detachEvent('onreadystatechange', arguments.callee);
			}
			else{
				ele.removeEventListener('load', arguments.callee, false);
				ele.removeEventListener('error', arguments.callee, false);
			}
			
			if(!isCss) docHead.removeChild(ele);
			if(isFunction(onLoad)) onLoad();
		}
	},
	
	pollCss : function(ele, onLoad){
		var me = this,
			loaded = !!ele.sheet;
		
		setTimeout(function(){
			if(loaded){
				if(isFunction(onLoad)) onLoad();
			}
			else{
				me.pollCss(ele, onLoad);
			}
		}, 50);
	},
	
	toString : function(){return "[object orderjs.fileLoader]";}
};

/**
 * A stack to manage loaded modules' execution.
 */
var execStack = orderjs.execStack = {
	_modules : [],
	
	executedList : [],
	
	/**
	 * Add new unexecuted module to stack.
	 */
	push : function(module){
		var modules = this._modules;
		
		if(module && !this.contains(module)){
			var rs = module.readyState;
			if(rs < 3){
				modules.push(module);
				this.checkExecution();
			}
		}
		
		return module;
	},
	
	pop : function(){
		return this._modules.pop();
	},
	
	peek : function(){
		var modules = this._modules;
		return modules[modules.length - 1];
	},
	
	isExecuting : function(module){
		return this.peek() == module;
	},
	
	isEmpty : function(){
		return !this._modules.length;
	},
	
	contains : function(module){
        var modules = this._modules;
		if(modules.indexOf) return modules.indexOf(module) > -1;
		for(var i=0,len=modules.length;  i<len; i++){
			if(modules[i] === module)return true;
		}
		return false;
	},
	
	/**
	 * Check to see if the top module executable (module's readyState >=2 and module.isDepsComplete()),
	 * if true, call module's exec() method, 
	 * else check its deps' readyState to make sure all of them are ready for execution.
	 */
	checkExecution : function(){
		var rs,
			module = this.peek();
		
		if(module){
			rs = module.readyState;
			if(rs >=2 && module.isDepsComplete()){
				this.doExecution(module);
			}
			else{
				this.checkDeps(module);
				if(rs == 0) module.load();
			}
		}
	},
	
	/**
	 * Check execution on module ready.
	 */
	checkExecutionOnModuleReady : function(module){
		var executing = this.peek();
		
		if(executing){
			if(executing == module || executing.readyState == 0) this.checkExecution();
		}
	},
	
	/**
	 * Execute module source, pop the module,
	 * and then start to check next executable, ignoring any execution error.
	 */
	doExecution : function(module){
		this.executedList.push(module);
		try{
			module.exec();
		}catch(err){
			this.hndExecErr(err, module);
		}
		finally{
			this.pop();
			
			if(this.isEmpty()){
				moduleMgr.checkOrderListExec(module);
			}
			else{
				this.checkExecution();
			}
		}
	},
	
	/**
	 * Check module's deps' readyState, to make sure all of them ready for execution.
	 * if any dep is unready, then call its load() method.
	 */
	checkDeps : function(module){
		each(module.getDeps(), this.checkDep, this);
	},
	
	checkDep : function(dep){
		var depModule = moduleMgr.getByName(dep);
		if(depModule){
			var rs = depModule.readyState;
			if(rs < 3){
				this.push(depModule);
				if(rs == 0){
					depModule.load();
				}
				else{
					this.checkExecution();
				}
				return false;
			}
		}
	},
	
	hndExecErr : function(err, module){
		throw err;
	},
	
	toString : function(){return "[object orderjs.execStack]";}
};

/**
 * @class Module
 * @constructor
 * @description Construct a instance by specified config.
 * @param {String | Object | Function} [uri] Module uri.
 *  if provided an object value, all of the members will be merged to module instance.
 *  if provided a function, means it's a local module without loading source from server.
 */
orderjs.Module = function(uri, isCss){
	this.initialize.apply(this, arguments);
};
orderjs.Module.prototype = {
	/** @lends orderjs.Module# */
	uri : "",
	
	isCss : false,
		
	/**
	 * (null) Module required modules.
	 * @type Array
	 */
	deps : null,
	
	/**
	 * (null) Source code of module.
	 * @type Function
	 */
	source : null,
	
	/**
	 * Indicates module state for execution:
	 * 0 : init;
	 * 1 : loading;
	 * 2 : ready, module source is ready( may be loaded or defined an anonymous);
	 * 3 : complete; module and its deps (if has) are all executed.
	 */
	readyState : 0,
	
	initialize : function(uri, isCss){
		this.track = bind(this.track, this);
		if(isFunction(uri)){
			this.source = uri;
			this.fireReady();
		}
		else{
			if(isObject(uri)){
				ext(this, uri);
				if(this.shim){
					this.define(this.shim.deps);
				}
			}
			else{
				this.uri = uri;
				if(isCss) this.isCss = true;
			}
		}
	},
	
	/**
	 * Start to load module source if it canStartLoad().
	 */
	load : function(){
		if(this.readyState >= 1 || !this.canStartLoad()) return ;
		this.readyState = 1;
		fileLoader.load(this.uri, this.isCss, bind(this.fireReady, this));
	},
	
	/**
	 * If module has shimmed deps, and they're not ready, returns false.
	 */
	canStartLoad : function(){
		if(this.shim){
			return !this.hasDeps() || this.isDepsComplete();
		}
		
		return true;
	},
	
	/**
	 * Defines module deps and source content.
	 * @param {Array} deps Module deps, if module has no deps, this param can be ommitted.
	 * @param {Function} source Module main source.
	 */
	define : function(deps, source){
		if(isFunction(deps)) this.source = deps;
		else{
			if(isArray(deps)){
				this.deps = filter(deps, function(dep){return isNonNullStr(dep);});
			}
			else if(isNonNullStr(deps)){
				this.deps = [deps];
			}
			if(isFunction(source)) this.source = source;
		}
	},
	
	/**
	 * Executes this source.
	 * Fires "exec" event.
	 */
	exec : function(){
		if(this.readyState >= 3) return ;
		this.readyState = 3;
		
		if(isFunction(this.source)){
			var depExports = map(this.getDeps(), function(dep){
				var exports,
					module = moduleMgr.getByName(dep);
				
				if(module) exports = module.exports;
				
				return exports || {};
			});
			this.exports = this.source.apply(this, depExports);
		}
		else if(this.shim && isNonNullStr(this.shim.exports)){
			this.exports = glob[this.shim.exports];
		}
	},
	
	/**
	 * Returns true if this deps (if has) are all complete.
	 */
	isDepsComplete : function(){
		var complete = true;
		if(this.hasDeps()){
			each(this.getDeps(), function(dep){
				var depModule = moduleMgr.getByName(dep);
				if(!depModule || depModule.readyState < 3){
					complete = false;
					return false;
				}
			}, this);
		}
		
		return complete;
	},
	
	hasDeps : function(){
		return !!(this.getDeps().length > 0);
	},
	
	getDeps : function(){
		return isArray(this.deps) ? this.deps : [];
	},
	
	/**
	 * Notify moduleMgr module ready.
	 */
	fireReady : function(){
		if(this.readyState >= 2) return ;
		this.readyState = 2;
		moduleMgr.hndReady(this);
	},
	
	/**
	 * Adding this.name for error track info which is helpful for debug.
	 * @see orderjs.track
	 */
	track : function(thisObj, method, errObj){
		return orderjs.track(this.name, thisObj, method, errObj);
	},
	
	toString : function(){return "[object orderjs.Module]";}

};

var rModule = /^(?!.+?\.(?:js|css)$)[^\/\?]*$/i,//not ends with ".js", ".css" nor contains "/", "?".
	rModSegment = /^(?:(.+)>)?(?:(.+):)?(.+)$/i,//"extension>projNS:moduleName"
	rUrl =/([^:>]+:\/\/.+)|([^:>]*\.\/.+)|([^:>]*(?:\/|\?).+)|([^:\/>]+\.css$)|([^:\/>]+\.js$)/i;
var moduleMgr = orderjs.moduleMgr = {
	/** @lends orderjs.moduleMgr */
	/**
	 * Base deps, unless these deps are all complete, the normal ordered modules wont start to load.
	 */
	_baseDeps : isNonNullStr(dataMain) ? [trim(dataMain)] : [],
	
	/**
	 * @name orderjs.configulation
	 */
	_config : {
		/** @lends orderjs.configulation */
		/**
		 * (false) Enable LAMD(Loose Asynchronous Module Definition) api.
		 * If true, orderjs.require and orderjs.define will be exposed to global,
		 * and you can call these methods directly, like:
		 * <code>
		 	require("jquery");
		 	define("myModule", [], function(){});
		 * </code>
		 */
		enableLAMD : false,
		
		/**
		 * AMD support details, works only if enableLAMD is true.
		 */
		AMD : null,
		
		disableCache : false,
		debugMode : false,
		
		/**
		 * Default css and js root for modules, if no rootNS is specified in module name.
		 */
		root : {
			js : orderjsRoot,
			css : orderjsRoot
		},
		
		/**
		 * Shim for non Orderjs standard modules to make them orderable and exportable.
		 * Config it with module name as property, detail object as value.
		 * The detail object has these properties:
		 * {Array} [deps] Specify module deps, module cant start to load unless all deps complete.
		 * {String} [exports] Global variable defined in module, so that we can reference it.
		 * @example
		 * To shim for "jQuery" and "jQuery-ui" modules, you can:
		 * <code>
		 	orderjs.config("shim", {
				"jQuery" : {
					exports : "jQuery"
				},
				"jQuery-ui" : {
					deps : ["jQuery"],
					exports : "jQuery"
				}
			});
		 * </code>
		 */
		shim : {},
		
		/**
		 * Prior path for special modules. You can use cross domain urls here, like public cnd.
		 * @example
		 * use baidu cnd for jquery modules, you can:
		 * <code>
			orderjs.config("paths", {
				"jquery" : "http://libs.baidu.com/jquery/1.4.2/jquery.js",
				"jquery-ui" : "http://libs.baidu.com/jqueryui/1.10.2/jquery-ui.min.js"
			});
		 * </code>
		 */
		paths : {}
	},
	
	_moduleRoots : {},
	
	/**
	 * Stores modules indexed with module uri.
	 */
	_modules : {},
	
	/**
	 * Modules ordered by orderjs() method;
	 */
	_normalOrderList : [],
	
	/**
	 * Stores modules imported by calling the require() method.
	 */
	_orderList : [],
	
	/**
	 * Stores parsed names to improve performance for parseName() method.
	 */
	_parsedNames : {},
	
	/**
	 * Stores ready modules, for debug only.
	 */
	readyList : [],
	
	/**
	 * If the require is in the "requireBase" mode, adds modules as base deps,
	 * or else just require the modules as normal.
	 */
	addBase : function(names){
		if(!this._baseDepsLoading || this._baseDepsComplete){
			return this.require.apply(this, arguments);
		}
		var baseDeps = this._baseDeps,
			deps = isArray(names) ? names : arguments;
		each(deps, function(dep){if(isNonNullStr(dep) || isFunction(dep))baseDeps.push(dep);});
		
		this.require(baseDeps);
	},
	
	/**
	 * Apply config detail to {@link orderjs.configulation}.
	 * @param {Object} options Config detail.
	 * @param {Object | String} [options.root] Config the default root (relative to order.js root).
	 *  if provided an object value, use the property "js" or "css" for the root.
	 *  if provided a string value, means for both js && css root.
	 * @param {Object} [options.paths] Config the paths with object for values, overrides the same configed.
	 * @param {Object} [options.shim] Config the shim with object for values, overrides the same configed.
	 * Note: any other configs will be extended directly to orderjs.configulation.
	 */
	applyConfig : function(options){
		if(!isObject(options)) return;
		var applyOpt = ext({}, options),
			cfg = this._config,
			optRoot = applyOpt.root,
			cfgRoot = cfg.root;
		
		if(isNonNullStr(optRoot)){
			cfgRoot.js = cfgRoot.css = absolutizePath(optRoot);
		}
		else{
			for(var i in optRoot){
				cfgRoot[i.toLowerCase()] = absolutizePath(optRoot[i]);
			}
		}
		delete applyOpt.root;
		
		ext(cfg.paths, applyOpt.paths);
		delete applyOpt.paths;
		
		ext(cfg.shim, applyOpt.shim);
		delete applyOpt.shim;
		
		ext(cfg, applyOpt);
	},
	
	/**
	 * Config Orderjs.
	 * @param {String | Object} name Config name.
	 *  Provide string value for config one, and an object value for batch.
	 * @see
	 *  #applyConfig
	 */
	config : function(name, value){
		var len = arguments.length;
		if(len == 2){
			if(isString(name)){
				var options = {};
				options[name] = value;
				this.applyConfig(options);
			}
		}
		else if(len == 1){
			if(isString(name)) return this._config[name];
			if(isObject(name)) this.applyConfig(name);
		}
	},
	
	/**
	 * Clear complete modules from order list, then add uncompletes to execStack.
	 */
	checkOrderListExec : function(){
		var module, orderList;
		
		if(execStack.isEmpty()){
			orderList = this._orderList;
			
			while(module = orderList[0]){
				if(module.readyState >=3){
					orderList.shift();
					continue;
				}
				execStack.push(module);
				return;
			}
			
			if(this._baseDepsLoading){
				this.endRequireBase();
			}
		}
	},
	
	/**
	 * Creates a module by valid module name or a function.
	 */
	create: function(name){
		var module;
		if(isNonNullStr(name)){
			name = trim(name);
			var opt,
				parsedName = this.parseName(name),
				uri = parsedName.uri,
				isCss = parsedName.type == "css",
				s = this._modules;
			
			if(isNonNullStr(uri)){
				if(s[uri]) module = s[uri];
				else{
					opt = {
						uri : uri,
						name : name,
						isCss : isCss,
						shim : this._config.shim[name]
					};
					
					module = s[uri] = new orderjs.Module(opt);
				}
			}
		}
		else if(isFunction(name)){
			module = new orderjs.Module(name);
		}
		
		if(!module){
			orderjs.debug(this + '>create(): Invalid name = ' + name + '', "err");
		}
		
		return module;
	},
	
	/**
	 * Defines named module deps and source.
	 */
	define : function(name, deps, source){
		var module = this.getByName(name);
		if(!module){
			module = this.create(name);
		}
		
		if(module){
			module.define(deps, source);
			if(module.hasDeps()) this.loadDeps(module);
		}
		
		return module;
	},
	
	/**
	 * End the "requireBase" mode.
	 * If the config enableLAMD is set to true by base deps, then turn it on,
	 * and then start to require normal orders.
	 */
	endRequireBase : function(){
		this._baseDepsLoading = false;
		this._baseDepsComplete = true;
		
		if(this._config.enableLAMD){
			ext(glob, {
				require : orderjs,
				define : orderjs.define
			});
			ext(glob.define, {
				AMDType : "LAMD",
				AMD : ext({}, this._config.AMD)
			});
		}
		
		this.require(this._normalOrderList);
	},
	
	/**
	 * Get defined module by an absolute uri.
	 */
	get : function(uri){
		return this._modules[uri];
	},
	
	/**
	 * Get defined module by a module name.
	 */
	getByName : function(name){
		return this.get(this.nameToUri(name));
	},
	
	/**
	 * Get registered module root by rootNS and type.
	 * @param {String} [rootNS] Root namespace, if not provided, use the default root.
	 * @param {String} [type="js"] Type of module: "css" or "js".
	 */
	getModuleRoot : function(rootNS, type){
		var root, roots;
		
		type = isNonNullStr(type) ? trim(type).toLowerCase() : "js";
		if(isNonNullStr(rootNS)){
			roots = this._moduleRoots[trim(rootNS).toLowerCase()];
		}
		
		return (roots || this._config.root)[type] || "";
	},
	
	/**
	 * Bridge method bt orderjs.Module and orderjs.execStack on module ready.
	 * @see orderjs.execStack.hndReady
	 */
	hndReady : function(module){
		this.readyList.push(module);
		execStack.checkExecutionOnModuleReady(module);
	},
	
	/**
	 * Start to load one module.
	 */
	load : function(name){
		var module;
		
		if(isNonNullStr(name)){
			module = this.getByName(name);
		}
		
		if(!module) module = this.create(name);
		
		if(module){
			if(module.canStartLoad()) module.load();
			else if(module.hasDeps()) this.loadDeps(module);
		}
		
		return module;
	},
	
	loadDeps : function(module){
		each(module.getDeps(), this.load, this);
	},
	
	/**
	 * Get type from module name.
	 * @see 
	 *  #parseName()
	 */
	nameToType : function(name){
		return this.parseName(name).type;
	},
	
	/**
	 * Get uri from module name.
	 * @see 
	 *  #parseName()
	 */
	nameToUri : function(name){
		return this.parseName(name).uri;
	},
	
	/**
	 * Parse module name to readable object, which may contain following members:
	 * {String} uri Module uri.
	 * {String} type Module type: "js" or "css".
	 * {String} rootNS Module root namespace.
	 * @param {String} name Module name represention.
	 *  The represention is formed of 3 parts: "extension>projNS:moduleName"
	 *  The "extension" specified the type of module, separated by ">".
	 *  The "rootNS" specified the root namespace of module, separated by ":".
	 *  You should register the namespace first.
	 *  The moduleName specified the path(relative to the rootNS) 
	 *  and module file name(without exetension), 
	 *  the directory separator of the path is "." not "/".
	 *  If any following condition occurs in the name represention,
	 *  it's treat as an uri without any convert(ignore the "extension" and "projNS")
	 *  1. name ends with ".js", ".css"
	 *  2. name contains "/", "?"
	 */
	parseName : function(name){
		if(!isString(name)) return {};
		name = trim(name);
		if(!isNonNullStr(name)) return {};
		if(this._config.paths[name]) name = this._config.paths[name];
		
		var moduleMatch, urlMatch, sgMatch,
			moduleName, type, root, rootNS,
			result = this._parsedNames[name];
		
		if(result) return result;
		result = {};
		
		if(moduleMatch = name.match(rModule)){
			//orderjs.dir(moduleMatch, "moduleMatch");
			if(sgMatch = name.match(rModSegment)){
				//orderjs.dir(sgMatch, "sgMatch");
				result.name = moduleName = trim((sgMatch[3] || ""));
				if(isNonNullStr(moduleName)){
					result.type = type = sgMatch[1] || "js";
					result.rootNS = rootNS = sgMatch[2];
					root = this.getModuleRoot(rootNS, type);
					result.uri = root + moduleName.replace(/\./g, "/") + "." + type;
				}
			}
			
			//orderjs.debug(this + ' name = ' + name + ' moduleMatch = ' + moduleMatch[0] + '');
			//orderjs.dir(name.match(rModSegment), name );
			//orderjs.dir(moduleMatch, name);
		}
		else if(urlMatch = name.match(rUrl)){
			//orderjs.dir(urlMatch, name);
			result.type = (urlMatch[4]) ? "css" : "js";
			result.uri = absolutizePath(urlMatch[0], this._config.root[result.type]);
		}
		
		if(result.uri){
			this._parsedNames[name] = result;
		}
		else if(orderjs.config("debugMode")){
			orderjs.debug(this + '>parseName(): Invalid name = ' + name + '');
		}
		
		return result;
	},
	
	/**
	 * Register a short name(namespace) for roots.
	 * @param {String} ns Short name used in moudle name.
	 * @param {Object | String} roots Specify the css or js root(relative to configed default root).
	 *  if provided an object value, use the property "js" or "css" for the root.
	 *  if provided a string value, means for both js && css root.
	 */
	regRootNS : function(ns, roots){
		if(!isNonNullStr(ns)) return ;
		if(isNonNullStr(roots)) roots = {js : roots, css : roots};
		else if(!isObject(roots)) return ;
		var registered, parsedRootNS,
			parsedNames = this._parsedNames,
			moduleRoots = this._moduleRoots;
		
		ns = ns.toLowerCase();
		if(!moduleRoots[ns]){
			moduleRoots[ns] = {};
		}
		registered = moduleRoots[ns];
		
		for(var i in roots){
			registered[i.toLowerCase()] = absolutizePath(roots[i], this._config.root[i]);
		}
		
		//clear old parsedNames that within ns root.
		for(var i in parsedNames){
			parsedRootNS = this.parseName(i).rootNS;
			if(parsedRootNS && parsedRootNS.toLowerCase() == ns){
				delete parsedNames[i];
			}
		}
	},
	
	/**
	 * Start to load module(s) by providing a names array or a list of names.
	 * @param {String | Array} name Module name, can be a defined module name or an url.
	 * @example
	 * <code>
	 	//you can call in this way:
		orderjs.require(["form.widgets", "css>form.widgets"]);
		//or in this way:
		orderjs.require("form.widgets", "css>form.widgets");
		//by url
		orderjs.require("http://domain.com/js/form/widgets.js");
		orderjs.require("/js/form/widgets.js");
		orderjs.require("/css/form/widgets.css");
	 * </code>
	 * @see
	 *  #orderjs.moduleMgr.parseName for more name parsing rules detais.
	 * @return {Object} 
	 *  if the first required module has been executed and defined an exports, returns the exports.
	 */
	require : function(names){
		var firstExports, module,
			args = isArray(names) ? names : arguments;
		
		if(isNonNullStr(args[0])){
			module = this.getByName(args[0]);
			if(module){
				firstExports = module.exports;
				if(args.length == 1) return firstExports;
			}
		}
		
		if(this._baseDepsLoading){
			if(names === this._baseDeps){
				//clear b4 next base require.
				this._baseDeps = [];
			}
			else{
				//push deps to normal orderList.
				var nList = this._normalOrderList;
				return each(args, function(name){nList.push(name);});
			}
		}
		each(args, function(name){
			module = this.load(name);
			
			if(module && module.readyState < 3){
				this._orderList.push(module);
				this.checkOrderListExec();
			}
		}, this);
		
		return firstExports;
	},
	
	/**
	 * Set loading mode of require to "requireBase",
	 * to stop normal orders from starting to load.
	 * then call the require to load base deps.
	 */
	startRequireBase : function(){
		if(this._baseDepsComplete) return;
		if(this._baseDeps.length == 0) return this.endRequireBase();
		
		this._baseDepsLoading = true;
		this.require(this._baseDeps);
	},
	
	toString : function(){return "[object orderjs.moduleMgr]";}
};

each(["define", "require", "addBase", "regRootNS", "config"], function(method){
	orderjs[method] = bind(moduleMgr[method], moduleMgr);
});

ext(orderjs, {	
	/** @lends orderjs */
	/**
	 * Framework version code.
	 */
	version : "0.9.2",
	
	revision : "236",
	
	/**
	 * Reference to orderjsNode, may be useful for framework developers.
	 * @type HTMLScriptElement
	 */
	orderjsNode : orderjsNode,
	
	/**
	 * Map path to absolute path.
	 * @param {String} path Path to be mapped, always be a relative path.
	 * @param {String} [root] If the path is a relative path, use this root for convert.
	 *  defaults to #orderjsRoot.
	 * @example
	 * var root = "http://domain.com/resource/js/";
	 * var cssRoot = absolutizePath("../css/", root);
	 * alert(cssRoot);//"http://domain.com/resource/css/";
	 */
	absolutizePath : absolutizePath,
	
	/**
	 * Set global orderjs to previous defined.
	 */
	noConflict : function(){
		glob.orderjs = glob.originalOrderjs;
	},
	
	/**
	 * Outputs msg on debug console.
	 * Debug helper fn, implements by users.
	 * @param {any} msg Message to be output;
	 * @param {String} [type="msg"] Message type, values: "msg", "warn", "err";
	 */
	debug : emptyFn,
	
	/**
	 * Print all members of an object on debug console.
	 * Debug helper fn, implements by users.
	 * @param 
	 */
	dir : emptyFn,
	
	/**
	 * Make error track info which is helpful for debug the detail of error.
	 * Debug helper fn, implements by users.
	 * @param {String} [name] Module name.
	 * @param {Object} [thisObj] The scope object of the erroring function.
	 * @param {String} [method] The name of the erroring function.
	 * @param {Object} [errObj] The invalid object cause the error.
	 */
	track : emptyFn

});

glob.orderjs = orderjs;
moduleMgr.startRequireBase();

}).call(this);
